/*
* Copyright (C) 1997-2001 Id Software, Inc.
* 
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
* 
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE.
* 
* See the GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307, USA.
*  
*/

// Created on 02.01.2004 by RST.

// $Id: CM.java,v 1.12 2005/12/04 21:00:17 cawe Exp $
using System;
using Defines = Suake2.UI.Defines;
using Globals = Suake2.UI.Globals;
using Suake2.UI.game;
using Suake2.UI.util;
//UPGRADE_TODO: The package 'java.nio' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using java.nio;
namespace Suake2.UI.qcommon
{
	
	public class CM
	{
		public CM()
		{
			InitBlock();
		}
		private void  InitBlock()
		{
			map_brushsides = ;
			map_surfaces = ;
			map_planes = ;
			map_nodes = ;
			map_leafs = ;
			map_leafbrushes = ;
			map_cmodels = ;
			map_brushes = ;
			map_visibility = ;
			map_areas = ;
			map_areaportals = ;
			portalopen = ;
			pvsrow = ;
			phsrow = ;
		}
		
		public class cnode_t
		{
			internal cplane_t plane; // ptr
			
			internal int[] children = new int[]{0, 0}; // negative numbers are leafs
		}
		
		public class cbrushside_t
		{
			internal cplane_t plane; // ptr
			
			internal mapsurface_t surface; // ptr
		}
		
		public class cleaf_t
		{
			internal int contents;
			
			internal int cluster;
			
			internal int area;
			
			// was unsigned short, but is ok (rst)
			internal short firstleafbrush;
			
			// was unsigned short, but is ok (rst)
			internal short numleafbrushes;
		}
		
		public class cbrush_t
		{
			internal int contents;
			
			internal int numsides;
			
			internal int firstbrushside;
			
			internal int checkcount; // to avoid repeated testings
		}
		
		public class carea_t
		{
			internal int numareaportals;
			
			internal int firstareaportal;
			
			internal int floodnum; // if two areas have equal floodnums, they are connected
			
			internal int floodvalid;
		}
		
		internal static int checkcount;
		
		internal static System.String map_name = "";
		
		internal static int numbrushsides;
		
		//UPGRADE_NOTE: The initialization of  'map_brushsides' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal static cbrushside_t[] map_brushsides;
		
		public static int numtexinfo;
		
		//UPGRADE_NOTE: The initialization of  'map_surfaces' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static mapsurface_t[] map_surfaces;
		
		internal static int numplanes;
		
		/// <summary>Extra for box hull ( +6) </summary>
		//UPGRADE_NOTE: The initialization of  'map_planes' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal static cplane_t[] map_planes;
		
		internal static int numnodes;
		
		/// <summary>Extra for box hull ( +6) </summary>
		//UPGRADE_NOTE: The initialization of  'map_nodes' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal static cnode_t[] map_nodes;
		
		internal static int numleafs = 1; // allow leaf funcs to be called without a map
		
		//UPGRADE_NOTE: The initialization of  'map_leafs' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		internal static cleaf_t[] map_leafs;
		
		internal static int emptyleaf;
		internal static int solidleaf;
		
		internal static int numleafbrushes;
		
		//UPGRADE_NOTE: The initialization of  'map_leafbrushes' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static int[] map_leafbrushes;
		
		public static int numcmodels;
		
		//UPGRADE_NOTE: The initialization of  'map_cmodels' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static cmodel_t[] map_cmodels;
		
		public static int numbrushes;
		
		//UPGRADE_NOTE: The initialization of  'map_brushes' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static cbrush_t[] map_brushes;
		
		public static int numvisibility;
		
		//UPGRADE_NOTE: The initialization of  'map_visibility' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static sbyte[] map_visibility;
		
		/// <summary>Main visibility data. </summary>
		public static qfiles.dvis_t map_vis = new qfiles.dvis_t(ByteBuffer.wrap(map_visibility));
		
		public static int numentitychars;
		
		public static System.String map_entitystring;
		
		public static int numareas = 1;
		
		//UPGRADE_NOTE: The initialization of  'map_areas' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static carea_t[] map_areas;
		
		public static int numareaportals;
		
		//UPGRADE_NOTE: The initialization of  'map_areaportals' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static qfiles.dareaportal_t[] map_areaportals;
		
		public static int numclusters = 1;
		
		public static mapsurface_t nullsurface = new mapsurface_t();
		
		public static int floodvalid;
		
		//UPGRADE_NOTE: The initialization of  'portalopen' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static bool[] portalopen;
		
		public static cvar_t map_noareas;
		
		public static sbyte[] cmod_base;
		
		public static int checksum;
		
		public static int last_checksum;
		
		/// <summary> Loads in the map and all submodels.</summary>
		public static cmodel_t CM_LoadMap(System.String name, bool clientload, int[] checksum)
		{
			Com.DPrintf("CM_LoadMap(" + name + ")...\n");
			sbyte[] buf;
			int i;
			qfiles.dheader_t header;
			int length;
			
			map_noareas = Cvar.Get("map_noareas", "0", 0);
			
			if (map_name.Equals(name) && (clientload || 0 == Cvar.VariableValue("flushmap")))
			{
				
				checksum[0] = last_checksum;
				
				if (!clientload)
				{
					SupportClass.ArraySupport.Fill(portalopen, false);
					FloodAreaConnections();
				}
				return map_cmodels[0]; // still have the right version
			}
			
			// free old stuff
			numnodes = 0;
			numleafs = 0;
			numcmodels = 0;
			numvisibility = 0;
			numentitychars = 0;
			map_entitystring = "";
			map_name = "";
			
			if (name == null || name.Length == 0)
			{
				numleafs = 1;
				numclusters = 1;
				numareas = 1;
				checksum[0] = 0;
				return map_cmodels[0];
				// cinematic servers won't have anything at all
			}
			
			//
			// load the file
			//
			buf = FS.LoadFile(name);
			
			if (buf == null)
				Com.Error(Defines.ERR_DROP, "Couldn't load " + name);
			
			length = buf.Length;
			
			ByteBuffer bbuf = ByteBuffer.wrap(buf);
			
			last_checksum = MD4.Com_BlockChecksum(buf, length);
			checksum[0] = last_checksum;
			
			header = new qfiles.dheader_t(bbuf.slice());
			
			if (header.version != Defines.BSPVERSION)
				Com.Error(Defines.ERR_DROP, "CMod_LoadBrushModel: " + name + " has wrong version number (" + header.version + " should be " + Defines.BSPVERSION + ")");
			
			cmod_base = buf;
			
			// load into heap
			CMod_LoadSurfaces(header.lumps[Defines.LUMP_TEXINFO]); // ok        
			CMod_LoadLeafs(header.lumps[Defines.LUMP_LEAFS]);
			CMod_LoadLeafBrushes(header.lumps[Defines.LUMP_LEAFBRUSHES]);
			CMod_LoadPlanes(header.lumps[Defines.LUMP_PLANES]);
			CMod_LoadBrushes(header.lumps[Defines.LUMP_BRUSHES]);
			CMod_LoadBrushSides(header.lumps[Defines.LUMP_BRUSHSIDES]);
			CMod_LoadSubmodels(header.lumps[Defines.LUMP_MODELS]);
			
			CMod_LoadNodes(header.lumps[Defines.LUMP_NODES]);
			CMod_LoadAreas(header.lumps[Defines.LUMP_AREAS]);
			CMod_LoadAreaPortals(header.lumps[Defines.LUMP_AREAPORTALS]);
			CMod_LoadVisibility(header.lumps[Defines.LUMP_VISIBILITY]);
			CMod_LoadEntityString(header.lumps[Defines.LUMP_ENTITIES]);
			
			FS.FreeFile(buf);
			
			CM_InitBoxHull();
			
			SupportClass.ArraySupport.Fill(portalopen, false);
			
			FloodAreaConnections();
			
			map_name = name;
			
			return map_cmodels[0];
		}
		
		/// <summary>Loads Submodels. </summary>
		public static void  CMod_LoadSubmodels(lump_t l)
		{
			Com.DPrintf("CMod_LoadSubmodels()\n");
			qfiles.dmodel_t in_Renamed;
			cmodel_t out_Renamed;
			int i, j, count;
			
			if ((l.filelen % qfiles.dmodel_t.SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "CMod_LoadBmodel: funny lump size");
			
			count = l.filelen / qfiles.dmodel_t.SIZE;
			
			if (count < 1)
				Com.Error(Defines.ERR_DROP, "Map with no models");
			if (count > Defines.MAX_MAP_MODELS)
				Com.Error(Defines.ERR_DROP, "Map has too many models");
			
			Com.DPrintf(" numcmodels=" + count + "\n");
			numcmodels = count;
			
			if (debugloadmap)
			{
				Com.DPrintf("submodles(headnode, <origin>, <mins>, <maxs>)\n");
			}
			for (i = 0; i < count; i++)
			{
				in_Renamed = new qfiles.dmodel_t(ByteBuffer.wrap(cmod_base, i * qfiles.dmodel_t.SIZE + l.fileofs, qfiles.dmodel_t.SIZE));
				out_Renamed = map_cmodels[i];
				
				for (j = 0; j < 3; j++)
				{
					// spread the mins / maxs by a pixel
					out_Renamed.mins[j] = in_Renamed.mins[j] - 1;
					out_Renamed.maxs[j] = in_Renamed.maxs[j] + 1;
					out_Renamed.origin[j] = in_Renamed.origin[j];
				}
				out_Renamed.headnode = in_Renamed.headnode;
				if (debugloadmap)
				{
					Com.DPrintf("|%6i|%8.2f|%8.2f|%8.2f|  %8.2f|%8.2f|%8.2f|   %8.2f|%8.2f|%8.2f|\n", new Vargs().add(out_Renamed.headnode).add(out_Renamed.origin[0]).add(out_Renamed.origin[1]).add(out_Renamed.origin[2]).add(out_Renamed.mins[0]).add(out_Renamed.mins[1]).add(out_Renamed.mins[2]).add(out_Renamed.maxs[0]).add(out_Renamed.maxs[1]).add(out_Renamed.maxs[2]));
				}
			}
		}
		
		internal static bool debugloadmap = false;
		
		/// <summary>Loads surfaces. </summary>
		public static void  CMod_LoadSurfaces(lump_t l)
		{
			Com.DPrintf("CMod_LoadSurfaces()\n");
			texinfo_t in_Renamed;
			mapsurface_t out_Renamed;
			int i, count;
			
			if ((l.filelen % texinfo_t.SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
			
			count = l.filelen / texinfo_t.SIZE;
			if (count < 1)
				Com.Error(Defines.ERR_DROP, "Map with no surfaces");
			if (count > Defines.MAX_MAP_TEXINFO)
				Com.Error(Defines.ERR_DROP, "Map has too many surfaces");
			
			numtexinfo = count;
			Com.DPrintf(" numtexinfo=" + count + "\n");
			if (debugloadmap)
				Com.DPrintf("surfaces:\n");
			
			for (i = 0; i < count; i++)
			{
				out_Renamed = map_surfaces[i] = new mapsurface_t();
				in_Renamed = new texinfo_t(cmod_base, l.fileofs + i * texinfo_t.SIZE, texinfo_t.SIZE);
				
				out_Renamed.c.name = in_Renamed.texture;
				out_Renamed.rname = in_Renamed.texture;
				out_Renamed.c.flags = in_Renamed.flags;
				out_Renamed.c.value_Renamed = in_Renamed.value_Renamed;
				
				if (debugloadmap)
				{
					Com.DPrintf("|%20s|%20s|%6i|%6i|\n", new Vargs().add(out_Renamed.c.name).add(out_Renamed.rname).add(out_Renamed.c.value_Renamed).add(out_Renamed.c.flags));
				}
			}
		}
		
		/// <summary>Loads nodes. </summary>
		public static void  CMod_LoadNodes(lump_t l)
		{
			Com.DPrintf("CMod_LoadNodes()\n");
			qfiles.dnode_t in_Renamed;
			int child;
			cnode_t out_Renamed;
			int i, j, count;
			
			if ((l.filelen % qfiles.dnode_t.SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size:" + l.fileofs + "," + qfiles.dnode_t.SIZE);
			count = l.filelen / qfiles.dnode_t.SIZE;
			
			if (count < 1)
				Com.Error(Defines.ERR_DROP, "Map has no nodes");
			if (count > Defines.MAX_MAP_NODES)
				Com.Error(Defines.ERR_DROP, "Map has too many nodes");
			
			numnodes = count;
			Com.DPrintf(" numnodes=" + count + "\n");
			
			if (debugloadmap)
			{
				Com.DPrintf("nodes(planenum, child[0], child[1])\n");
			}
			
			for (i = 0; i < count; i++)
			{
				in_Renamed = new qfiles.dnode_t(ByteBuffer.wrap(cmod_base, qfiles.dnode_t.SIZE * i + l.fileofs, qfiles.dnode_t.SIZE));
				out_Renamed = map_nodes[i];
				
				out_Renamed.plane = map_planes[in_Renamed.planenum];
				for (j = 0; j < 2; j++)
				{
					child = in_Renamed.children[j];
					out_Renamed.children[j] = child;
				}
				if (debugloadmap)
				{
					Com.DPrintf("|%6i| %6i| %6i|\n", new Vargs().add(in_Renamed.planenum).add(out_Renamed.children[0]).add(out_Renamed.children[1]));
				}
			}
		}
		
		/// <summary>Loads brushes.</summary>
		public static void  CMod_LoadBrushes(lump_t l)
		{
			Com.DPrintf("CMod_LoadBrushes()\n");
			qfiles.dbrush_t in_Renamed;
			cbrush_t out_Renamed;
			int i, count;
			
			if ((l.filelen % qfiles.dbrush_t.SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
			
			count = l.filelen / qfiles.dbrush_t.SIZE;
			
			if (count > Defines.MAX_MAP_BRUSHES)
				Com.Error(Defines.ERR_DROP, "Map has too many brushes");
			
			numbrushes = count;
			Com.DPrintf(" numbrushes=" + count + "\n");
			if (debugloadmap)
			{
				Com.DPrintf("brushes:(firstbrushside, numsides, contents)\n");
			}
			for (i = 0; i < count; i++)
			{
				in_Renamed = new qfiles.dbrush_t(ByteBuffer.wrap(cmod_base, i * qfiles.dbrush_t.SIZE + l.fileofs, qfiles.dbrush_t.SIZE));
				out_Renamed = map_brushes[i];
				out_Renamed.firstbrushside = in_Renamed.firstside;
				out_Renamed.numsides = in_Renamed.numsides;
				out_Renamed.contents = in_Renamed.contents;
				
				if (debugloadmap)
				{
					Com.DPrintf("| %6i| %6i| %8X|\n", new Vargs().add(out_Renamed.firstbrushside).add(out_Renamed.numsides).add(out_Renamed.contents));
				}
			}
		}
		
		/// <summary>Loads leafs.   </summary>
		public static void  CMod_LoadLeafs(lump_t l)
		{
			Com.DPrintf("CMod_LoadLeafs()\n");
			int i;
			cleaf_t out_Renamed;
			qfiles.dleaf_t in_Renamed;
			int count;
			
			if ((l.filelen % qfiles.dleaf_t.SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
			
			count = l.filelen / qfiles.dleaf_t.SIZE;
			
			if (count < 1)
				Com.Error(Defines.ERR_DROP, "Map with no leafs");
			
			// need to save space for box planes
			if (count > Defines.MAX_MAP_PLANES)
				Com.Error(Defines.ERR_DROP, "Map has too many planes");
			
			Com.DPrintf(" numleafes=" + count + "\n");
			
			numleafs = count;
			numclusters = 0;
			if (debugloadmap)
				Com.DPrintf("cleaf-list:(contents, cluster, area, firstleafbrush, numleafbrushes)\n");
			for (i = 0; i < count; i++)
			{
				in_Renamed = new qfiles.dleaf_t(cmod_base, i * qfiles.dleaf_t.SIZE + l.fileofs, qfiles.dleaf_t.SIZE);
				
				out_Renamed = map_leafs[i];
				
				out_Renamed.contents = in_Renamed.contents;
				out_Renamed.cluster = in_Renamed.cluster;
				out_Renamed.area = in_Renamed.area;
				out_Renamed.firstleafbrush = (short) in_Renamed.firstleafbrush;
				out_Renamed.numleafbrushes = (short) in_Renamed.numleafbrushes;
				
				if (out_Renamed.cluster >= numclusters)
					numclusters = out_Renamed.cluster + 1;
				
				if (debugloadmap)
				{
					Com.DPrintf("|%8x|%6i|%6i|%6i|\n", new Vargs().add(out_Renamed.contents).add(out_Renamed.cluster).add(out_Renamed.area).add(out_Renamed.firstleafbrush).add(out_Renamed.numleafbrushes));
				}
			}
			
			Com.DPrintf(" numclusters=" + numclusters + "\n");
			
			if (map_leafs[0].contents != Defines.CONTENTS_SOLID)
				Com.Error(Defines.ERR_DROP, "Map leaf 0 is not CONTENTS_SOLID");
			
			solidleaf = 0;
			emptyleaf = - 1;
			
			for (i = 1; i < numleafs; i++)
			{
				if (map_leafs[i].contents == 0)
				{
					emptyleaf = i;
					break;
				}
			}
			
			if (emptyleaf == - 1)
				Com.Error(Defines.ERR_DROP, "Map does not have an empty leaf");
		}
		
		/// <summary>Loads planes. </summary>
		public static void  CMod_LoadPlanes(lump_t l)
		{
			Com.DPrintf("CMod_LoadPlanes()\n");
			int i, j;
			cplane_t out_Renamed;
			qfiles.dplane_t in_Renamed;
			int count;
			int bits;
			
			if ((l.filelen % qfiles.dplane_t.SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
			
			count = l.filelen / qfiles.dplane_t.SIZE;
			
			if (count < 1)
				Com.Error(Defines.ERR_DROP, "Map with no planes");
			
			// need to save space for box planes
			if (count > Defines.MAX_MAP_PLANES)
				Com.Error(Defines.ERR_DROP, "Map has too many planes");
			
			Com.DPrintf(" numplanes=" + count + "\n");
			
			numplanes = count;
			if (debugloadmap)
			{
				Com.DPrintf("cplanes(normal[0],normal[1],normal[2], dist, type, signbits)\n");
			}
			
			for (i = 0; i < count; i++)
			{
				in_Renamed = new qfiles.dplane_t(ByteBuffer.wrap(cmod_base, i * qfiles.dplane_t.SIZE + l.fileofs, qfiles.dplane_t.SIZE));
				
				out_Renamed = map_planes[i];
				
				bits = 0;
				for (j = 0; j < 3; j++)
				{
					out_Renamed.normal[j] = in_Renamed.normal[j];
					
					if (out_Renamed.normal[j] < 0)
						bits |= 1 << j;
				}
				
				out_Renamed.dist = in_Renamed.dist;
				out_Renamed.type = (sbyte) in_Renamed.type;
				out_Renamed.signbits = (sbyte) bits;
				
				if (debugloadmap)
				{
					Com.DPrintf("|%6.2f|%6.2f|%6.2f| %10.2f|%3i| %1i|\n", new Vargs().add(out_Renamed.normal[0]).add(out_Renamed.normal[1]).add(out_Renamed.normal[2]).add(out_Renamed.dist).add(out_Renamed.type).add(out_Renamed.signbits));
				}
			}
		}
		
		/// <summary>Loads leaf brushes. </summary>
		public static void  CMod_LoadLeafBrushes(lump_t l)
		{
			Com.DPrintf("CMod_LoadLeafBrushes()\n");
			int i;
			int[] out_Renamed;
			short[] in_Renamed;
			int count;
			
			if ((l.filelen % 2) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
			
			count = l.filelen / 2;
			
			Com.DPrintf(" numbrushes=" + count + "\n");
			
			if (count < 1)
				Com.Error(Defines.ERR_DROP, "Map with no planes");
			
			// need to save space for box planes
			if (count > Defines.MAX_MAP_LEAFBRUSHES)
				Com.Error(Defines.ERR_DROP, "Map has too many leafbrushes");
			
			out_Renamed = map_leafbrushes;
			numleafbrushes = count;
			
			ByteBuffer bb = ByteBuffer.wrap(cmod_base, l.fileofs, count * 2).order(ByteOrder.LITTLE_ENDIAN);
			
			if (debugloadmap)
			{
				Com.DPrintf("map_brushes:\n");
			}
			
			for (i = 0; i < count; i++)
			{
				out_Renamed[i] = bb.getShort();
				if (debugloadmap)
				{
					Com.DPrintf("|%6i|%6i|\n", new Vargs().add(i).add(out_Renamed[i]));
				}
			}
		}
		
		/// <summary>Loads brush sides. </summary>
		public static void  CMod_LoadBrushSides(lump_t l)
		{
			Com.DPrintf("CMod_LoadBrushSides()\n");
			int i, j;
			cbrushside_t out_Renamed;
			qfiles.dbrushside_t in_Renamed;
			int count;
			int num;
			
			if ((l.filelen % qfiles.dbrushside_t.SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
			count = l.filelen / qfiles.dbrushside_t.SIZE;
			
			// need to save space for box planes
			if (count > Defines.MAX_MAP_BRUSHSIDES)
				Com.Error(Defines.ERR_DROP, "Map has too many planes");
			
			numbrushsides = count;
			
			Com.DPrintf(" numbrushsides=" + count + "\n");
			
			if (debugloadmap)
			{
				Com.DPrintf("brushside(planenum, surfacenum):\n");
			}
			for (i = 0; i < count; i++)
			{
				
				in_Renamed = new qfiles.dbrushside_t(ByteBuffer.wrap(cmod_base, i * qfiles.dbrushside_t.SIZE + l.fileofs, qfiles.dbrushside_t.SIZE));
				
				out_Renamed = map_brushsides[i];
				
				num = in_Renamed.planenum;
				
				out_Renamed.plane = map_planes[num]; // pointer
				
				j = in_Renamed.texinfo;
				
				if (j >= numtexinfo)
					Com.Error(Defines.ERR_DROP, "Bad brushside texinfo");
				
				// java specific handling of -1
				if (j == - 1)
					out_Renamed.surface = new mapsurface_t();
				// just for safety
				else
					out_Renamed.surface = map_surfaces[j];
				
				if (debugloadmap)
				{
					Com.DPrintf("| %6i| %6i|\n", new Vargs().add(num).add(j));
				}
			}
		}
		
		/// <summary>Loads areas. </summary>
		public static void  CMod_LoadAreas(lump_t l)
		{
			Com.DPrintf("CMod_LoadAreas()\n");
			int i;
			carea_t out_Renamed;
			qfiles.darea_t in_Renamed;
			int count;
			
			if ((l.filelen % qfiles.darea_t.SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
			
			count = l.filelen / qfiles.darea_t.SIZE;
			
			if (count > Defines.MAX_MAP_AREAS)
				Com.Error(Defines.ERR_DROP, "Map has too many areas");
			
			Com.DPrintf(" numareas=" + count + "\n");
			numareas = count;
			
			if (debugloadmap)
			{
				Com.DPrintf("areas(numportals, firstportal)\n");
			}
			
			for (i = 0; i < count; i++)
			{
				
				in_Renamed = new qfiles.darea_t(ByteBuffer.wrap(cmod_base, i * qfiles.darea_t.SIZE + l.fileofs, qfiles.darea_t.SIZE));
				out_Renamed = map_areas[i];
				
				out_Renamed.numareaportals = in_Renamed.numareaportals;
				out_Renamed.firstareaportal = in_Renamed.firstareaportal;
				out_Renamed.floodvalid = 0;
				out_Renamed.floodnum = 0;
				if (debugloadmap)
				{
					Com.DPrintf("| %6i| %6i|\n", new Vargs().add(out_Renamed.numareaportals).add(out_Renamed.firstareaportal));
				}
			}
		}
		
		/// <summary>Loads area portals. </summary>
		public static void  CMod_LoadAreaPortals(lump_t l)
		{
			Com.DPrintf("CMod_LoadAreaPortals()\n");
			int i;
			qfiles.dareaportal_t out_Renamed;
			qfiles.dareaportal_t in_Renamed;
			int count;
			
			if ((l.filelen % qfiles.dareaportal_t.SIZE) != 0)
				Com.Error(Defines.ERR_DROP, "MOD_LoadBmodel: funny lump size");
			count = l.filelen / qfiles.dareaportal_t.SIZE;
			
			if (count > Defines.MAX_MAP_AREAS)
				Com.Error(Defines.ERR_DROP, "Map has too many areas");
			
			numareaportals = count;
			Com.DPrintf(" numareaportals=" + count + "\n");
			if (debugloadmap)
			{
				Com.DPrintf("areaportals(portalnum, otherarea)\n");
			}
			for (i = 0; i < count; i++)
			{
				in_Renamed = new qfiles.dareaportal_t(ByteBuffer.wrap(cmod_base, i * qfiles.dareaportal_t.SIZE + l.fileofs, qfiles.dareaportal_t.SIZE));
				
				out_Renamed = map_areaportals[i];
				
				out_Renamed.portalnum = in_Renamed.portalnum;
				out_Renamed.otherarea = in_Renamed.otherarea;
				
				if (debugloadmap)
				{
					Com.DPrintf("|%6i|%6i|\n", new Vargs().add(out_Renamed.portalnum).add(out_Renamed.otherarea));
				}
			}
		}
		
		/// <summary>Loads visibility data. </summary>
		public static void  CMod_LoadVisibility(lump_t l)
		{
			Com.DPrintf("CMod_LoadVisibility()\n");
			int i;
			
			numvisibility = l.filelen;
			
			Com.DPrintf(" numvisibility=" + numvisibility + "\n");
			
			if (l.filelen > Defines.MAX_MAP_VISIBILITY)
				Com.Error(Defines.ERR_DROP, "Map has too large visibility lump");
			
			Array.Copy(cmod_base, l.fileofs, map_visibility, 0, l.filelen);
			
			ByteBuffer bb = ByteBuffer.wrap(map_visibility, 0, l.filelen);
			bb.order(ByteOrder.LITTLE_ENDIAN);
			
			map_vis = new qfiles.dvis_t(bb);
		}
		
		/// <summary>Loads entity strings. </summary>
		public static void  CMod_LoadEntityString(lump_t l)
		{
			Com.DPrintf("CMod_LoadEntityString()\n");
			
			numentitychars = l.filelen;
			
			if (l.filelen > Defines.MAX_MAP_ENTSTRING)
				Com.Error(Defines.ERR_DROP, "Map has too large entity lump");
			
			int x = 0;
			for (; x < l.filelen && cmod_base[x + l.fileofs] != 0; x++)
				;
			
			map_entitystring = new System.String(SupportClass.ToCharArray(cmod_base), l.fileofs, x).Trim();
			Com.dprintln("entitystring=" + map_entitystring.Length + " bytes, [" + map_entitystring.Substring(0, (System.Math.Min(map_entitystring.Length, 15)) - (0)) + "...]");
		}
		
		/// <summary>Returns the model with a given id "*" + <number> </summary>
		public static cmodel_t InlineModel(System.String name)
		{
			int num;
			
			if (name == null || name[0] != '*')
				Com.Error(Defines.ERR_DROP, "CM_InlineModel: bad name");
			
			num = Lib.atoi(name.Substring(1));
			
			if (num < 1 || num >= numcmodels)
				Com.Error(Defines.ERR_DROP, "CM_InlineModel: bad number");
			
			return map_cmodels[num];
		}
		
		public static int CM_NumClusters()
		{
			return numclusters;
		}
		
		public static int CM_NumInlineModels()
		{
			return numcmodels;
		}
		
		public static System.String CM_EntityString()
		{
			return map_entitystring;
		}
		
		public static int CM_LeafContents(int leafnum)
		{
			if (leafnum < 0 || leafnum >= numleafs)
				Com.Error(Defines.ERR_DROP, "CM_LeafContents: bad number");
			return map_leafs[leafnum].contents;
		}
		
		public static int CM_LeafCluster(int leafnum)
		{
			if (leafnum < 0 || leafnum >= numleafs)
				Com.Error(Defines.ERR_DROP, "CM_LeafCluster: bad number");
			return map_leafs[leafnum].cluster;
		}
		
		public static int CM_LeafArea(int leafnum)
		{
			if (leafnum < 0 || leafnum >= numleafs)
				Com.Error(Defines.ERR_DROP, "CM_LeafArea: bad number");
			return map_leafs[leafnum].area;
		}
		
		internal static cplane_t[] box_planes;
		
		internal static int box_headnode;
		
		internal static cbrush_t box_brush;
		
		internal static cleaf_t box_leaf;
		
		/// <summary>Set up the planes and nodes so that the six floats of a bounding box can
		/// just be stored out and get a proper clipping hull structure.
		/// </summary>
		public static void  CM_InitBoxHull()
		{
			int i;
			int side;
			cnode_t c;
			cplane_t p;
			cbrushside_t s;
			
			box_headnode = numnodes; //rst: still room for 6 brushes left?
			
			box_planes = new cplane_t[]{map_planes[numplanes], map_planes[numplanes + 1], map_planes[numplanes + 2], map_planes[numplanes + 3], map_planes[numplanes + 4], map_planes[numplanes + 5], map_planes[numplanes + 6], map_planes[numplanes + 7], map_planes[numplanes + 8], map_planes[numplanes + 9], map_planes[numplanes + 10], map_planes[numplanes + 11], map_planes[numplanes + 12]};
			
			if (numnodes + 6 > Defines.MAX_MAP_NODES || numbrushes + 1 > Defines.MAX_MAP_BRUSHES || numleafbrushes + 1 > Defines.MAX_MAP_LEAFBRUSHES || numbrushsides + 6 > Defines.MAX_MAP_BRUSHSIDES || numplanes + 12 > Defines.MAX_MAP_PLANES)
				Com.Error(Defines.ERR_DROP, "Not enough room for box tree");
			
			box_brush = map_brushes[numbrushes];
			box_brush.numsides = 6;
			box_brush.firstbrushside = numbrushsides;
			box_brush.contents = Defines.CONTENTS_MONSTER;
			
			box_leaf = map_leafs[numleafs];
			box_leaf.contents = Defines.CONTENTS_MONSTER;
			box_leaf.firstleafbrush = (short) SupportClass.Identity(numleafbrushes);
			box_leaf.numleafbrushes = 1;
			
			map_leafbrushes[numleafbrushes] = numbrushes;
			
			for (i = 0; i < 6; i++)
			{
				side = i & 1;
				
				// brush sides
				s = map_brushsides[numbrushsides + i];
				s.plane = map_planes[(numplanes + i * 2 + side)];
				s.surface = nullsurface;
				
				// nodes
				c = map_nodes[box_headnode + i];
				c.plane = map_planes[(numplanes + i * 2)];
				c.children[side] = - 1 - emptyleaf;
				if (i != 5)
					c.children[side ^ 1] = box_headnode + i + 1;
				else
					c.children[side ^ 1] = - 1 - numleafs;
				
				// planes
				p = box_planes[i * 2];
				p.type = (sbyte) (i >> 1);
				p.signbits = 0;
				Math3D.VectorClear(p.normal);
				p.normal[i >> 1] = 1;
				
				p = box_planes[i * 2 + 1];
				p.type = (sbyte) (3 + (i >> 1));
				p.signbits = 0;
				Math3D.VectorClear(p.normal);
				p.normal[i >> 1] = - 1;
			}
		}
		
		/// <summary>To keep everything totally uniform, bounding boxes are turned into small
		/// BSP trees instead of being compared directly. 
		/// </summary>
		public static int HeadnodeForBox(float[] mins, float[] maxs)
		{
			box_planes[0].dist = maxs[0];
			box_planes[1].dist = - maxs[0];
			box_planes[2].dist = mins[0];
			box_planes[3].dist = - mins[0];
			box_planes[4].dist = maxs[1];
			box_planes[5].dist = - maxs[1];
			box_planes[6].dist = mins[1];
			box_planes[7].dist = - mins[1];
			box_planes[8].dist = maxs[2];
			box_planes[9].dist = - maxs[2];
			box_planes[10].dist = mins[2];
			box_planes[11].dist = - mins[2];
			
			return box_headnode;
		}
		
		/// <summary>Recursively searches the leaf number that contains the 3d point. </summary>
		private static int CM_PointLeafnum_r(float[] p, int num)
		{
			float d;
			cnode_t node;
			cplane_t plane;
			
			while (num >= 0)
			{
				node = map_nodes[num];
				plane = node.plane;
				
				if (plane.type < 3)
					d = p[plane.type] - plane.dist;
				else
					d = Math3D.DotProduct(plane.normal, p) - plane.dist;
				if (d < 0)
					num = node.children[1];
				else
					num = node.children[0];
			}
			
			Globals.c_pointcontents++; // optimize counter
			
			return - 1 - num;
		}
		
		/// <summary>Searches the leaf number that contains the 3d point. </summary>
		public static int CM_PointLeafnum(float[] p)
		{
			// sound may call this without map loaded
			if (numplanes == 0)
				return 0;
			return CM_PointLeafnum_r(p, 0);
		}
		
		
		private static int leaf_count;
		private static int leaf_maxcount;
		
		private static int[] leaf_list;
		
		private static float[] leaf_mins;
		private static float[] leaf_maxs;
		
		private static int leaf_topnode;
		
		/// <summary>Recursively fills in a list of all the leafs touched. </summary>
		private static void  CM_BoxLeafnums_r(int nodenum)
		{
			cplane_t plane;
			cnode_t node;
			int s;
			
			while (true)
			{
				if (nodenum < 0)
				{
					if (leaf_count >= leaf_maxcount)
					{
						Com.DPrintf("CM_BoxLeafnums_r: overflow\n");
						return ;
					}
					leaf_list[leaf_count++] = - 1 - nodenum;
					return ;
				}
				
				node = map_nodes[nodenum];
				plane = node.plane;
				
				s = Math3D.BoxOnPlaneSide(leaf_mins, leaf_maxs, plane);
				
				if (s == 1)
					nodenum = node.children[0];
				else if (s == 2)
					nodenum = node.children[1];
				else
				{
					// go down both
					if (leaf_topnode == - 1)
						leaf_topnode = nodenum;
					CM_BoxLeafnums_r(node.children[0]);
					nodenum = node.children[1];
				}
			}
		}
		
		/// <summary>Fills in a list of all the leafs touched and starts with the head node. </summary>
		private static int CM_BoxLeafnums_headnode(float[] mins, float[] maxs, int[] list, int listsize, int headnode, int[] topnode)
		{
			leaf_list = list;
			leaf_count = 0;
			leaf_maxcount = listsize;
			leaf_mins = mins;
			leaf_maxs = maxs;
			
			leaf_topnode = - 1;
			
			CM_BoxLeafnums_r(headnode);
			
			if (topnode != null)
				topnode[0] = leaf_topnode;
			
			return leaf_count;
		}
		
		/// <summary>Fills in a list of all the leafs touched. </summary>
		public static int CM_BoxLeafnums(float[] mins, float[] maxs, int[] list, int listsize, int[] topnode)
		{
			return CM_BoxLeafnums_headnode(mins, maxs, list, listsize, map_cmodels[0].headnode, topnode);
		}
		
		/// <summary>Returns a tag that describes the content of the point. </summary>
		public static int PointContents(float[] p, int headnode)
		{
			int l;
			
			if (numnodes == 0)
			// map not loaded
				return 0;
			
			l = CM_PointLeafnum_r(p, headnode);
			
			return map_leafs[l].contents;
		}
		
		/*
		* ================== CM_TransformedPointContents
		* 
		* Handles offseting and rotation of the end points for moving and rotating
		* entities ==================
		*/
		public static int TransformedPointContents(float[] p, int headnode, float[] origin, float[] angles)
		{
			float[] p_l = new float[]{0, 0, 0};
			float[] temp = new float[]{0, 0, 0};
			float[] forward = new float[]{0, 0, 0}, right = new float[]{0, 0, 0}, up = new float[]{0, 0, 0};
			int l;
			
			// subtract origin offset
			Math3D.VectorSubtract(p, origin, p_l);
			
			// rotate start and end into the models frame of reference
			if (headnode != box_headnode && (angles[0] != 0 || angles[1] != 0 || angles[2] != 0))
			{
				Math3D.AngleVectors(angles, forward, right, up);
				
				Math3D.VectorCopy(p_l, temp);
				p_l[0] = Math3D.DotProduct(temp, forward);
				p_l[1] = - Math3D.DotProduct(temp, right);
				p_l[2] = Math3D.DotProduct(temp, up);
			}
			
			l = CM_PointLeafnum_r(p_l, headnode);
			
			return map_leafs[l].contents;
		}
		
		/*
		* ===============================================================================
		* 
		* BOX TRACING
		* 
		* ===============================================================================
		*/
		
		// 1/32 epsilon to keep floating point happy
		private const float DIST_EPSILON = 0.03125f;
		
		private static float[] trace_start = new float[]{0, 0, 0};
		private static float[] trace_end = new float[]{0, 0, 0};
		
		private static float[] trace_mins = new float[]{0, 0, 0};
		private static float[] trace_maxs = new float[]{0, 0, 0};
		
		private static float[] trace_extents = new float[]{0, 0, 0};
		
		private static trace_t trace_trace = new trace_t();
		
		private static int trace_contents;
		
		private static bool trace_ispoint; // optimized case
		
		/*
		* ================ CM_ClipBoxToBrush ================
		*/
		public static void  CM_ClipBoxToBrush(float[] mins, float[] maxs, float[] p1, float[] p2, trace_t trace, cbrush_t brush)
		{
			int i, j;
			cplane_t plane, clipplane;
			float dist;
			float enterfrac, leavefrac;
			float[] ofs = new float[]{0, 0, 0};
			float d1, d2;
			bool getout, startout;
			float f;
			cbrushside_t side, leadside;
			
			enterfrac = - 1;
			leavefrac = 1;
			clipplane = null;
			
			if (brush.numsides == 0)
				return ;
			
			Globals.c_brush_traces++;
			
			getout = false;
			startout = false;
			leadside = null;
			
			for (i = 0; i < brush.numsides; i++)
			{
				side = map_brushsides[brush.firstbrushside + i];
				plane = side.plane;
				
				// FIXME: special case for axial
				
				if (!trace_ispoint)
				{
					// general box case
					
					// push the plane out apropriately for mins/maxs
					
					// FIXME: use signbits into 8 way lookup for each mins/maxs
					for (j = 0; j < 3; j++)
					{
						if (plane.normal[j] < 0)
							ofs[j] = maxs[j];
						else
							ofs[j] = mins[j];
					}
					dist = Math3D.DotProduct(ofs, plane.normal);
					dist = plane.dist - dist;
				}
				else
				{
					// special point case
					dist = plane.dist;
				}
				
				d1 = Math3D.DotProduct(p1, plane.normal) - dist;
				d2 = Math3D.DotProduct(p2, plane.normal) - dist;
				
				if (d2 > 0)
					getout = true; // endpoint is not in solid
				if (d1 > 0)
					startout = true;
				
				// if completely in front of face, no intersection
				if (d1 > 0 && d2 >= d1)
					return ;
				
				if (d1 <= 0 && d2 <= 0)
					continue;
				
				// crosses face
				if (d1 > d2)
				{
					// enter
					f = (d1 - DIST_EPSILON) / (d1 - d2);
					if (f > enterfrac)
					{
						enterfrac = f;
						clipplane = plane;
						leadside = side;
					}
				}
				else
				{
					// leave
					f = (d1 + DIST_EPSILON) / (d1 - d2);
					if (f < leavefrac)
						leavefrac = f;
				}
			}
			
			if (!startout)
			{
				// original point was inside brush
				trace.startsolid = true;
				if (!getout)
					trace.allsolid = true;
				return ;
			}
			if (enterfrac < leavefrac)
			{
				if (enterfrac > - 1 && enterfrac < trace.fraction)
				{
					if (enterfrac < 0)
						enterfrac = 0;
					trace.fraction = enterfrac;
					// copy
					trace.plane.set_Renamed(clipplane);
					trace.surface = leadside.surface.c;
					trace.contents = brush.contents;
				}
			}
		}
		
		/*
		* ================ CM_TestBoxInBrush ================
		*/
		public static void  CM_TestBoxInBrush(float[] mins, float[] maxs, float[] p1, trace_t trace, cbrush_t brush)
		{
			int i, j;
			cplane_t plane;
			float dist;
			float[] ofs = new float[]{0, 0, 0};
			float d1;
			cbrushside_t side;
			
			if (brush.numsides == 0)
				return ;
			
			for (i = 0; i < brush.numsides; i++)
			{
				side = map_brushsides[brush.firstbrushside + i];
				plane = side.plane;
				
				// FIXME: special case for axial
				// general box case
				// push the plane out apropriately for mins/maxs
				// FIXME: use signbits into 8 way lookup for each mins/maxs
				
				for (j = 0; j < 3; j++)
				{
					if (plane.normal[j] < 0)
						ofs[j] = maxs[j];
					else
						ofs[j] = mins[j];
				}
				dist = Math3D.DotProduct(ofs, plane.normal);
				dist = plane.dist - dist;
				
				d1 = Math3D.DotProduct(p1, plane.normal) - dist;
				
				// if completely in front of face, no intersection
				if (d1 > 0)
					return ;
			}
			
			// inside this brush
			trace.startsolid = trace.allsolid = true;
			trace.fraction = 0;
			trace.contents = brush.contents;
		}
		
		/*
		* ================ CM_TraceToLeaf ================
		*/
		public static void  CM_TraceToLeaf(int leafnum)
		{
			int k;
			int brushnum;
			cleaf_t leaf;
			cbrush_t b;
			
			leaf = map_leafs[leafnum];
			if (0 == (leaf.contents & trace_contents))
				return ;
			
			// trace line against all brushes in the leaf
			for (k = 0; k < leaf.numleafbrushes; k++)
			{
				
				brushnum = map_leafbrushes[leaf.firstleafbrush + k];
				b = map_brushes[brushnum];
				if (b.checkcount == checkcount)
					continue; // already checked this brush in another leaf
				b.checkcount = checkcount;
				
				if (0 == (b.contents & trace_contents))
					continue;
				CM_ClipBoxToBrush(trace_mins, trace_maxs, trace_start, trace_end, trace_trace, b);
				if (0 == trace_trace.fraction)
					return ;
			}
		}
		
		/*
		* ================ CM_TestInLeaf ================
		*/
		public static void  CM_TestInLeaf(int leafnum)
		{
			int k;
			int brushnum;
			cleaf_t leaf;
			cbrush_t b;
			
			leaf = map_leafs[leafnum];
			if (0 == (leaf.contents & trace_contents))
				return ;
			// trace line against all brushes in the leaf
			for (k = 0; k < leaf.numleafbrushes; k++)
			{
				brushnum = map_leafbrushes[leaf.firstleafbrush + k];
				b = map_brushes[brushnum];
				if (b.checkcount == checkcount)
					continue; // already checked this brush in another leaf
				b.checkcount = checkcount;
				
				if (0 == (b.contents & trace_contents))
					continue;
				CM_TestBoxInBrush(trace_mins, trace_maxs, trace_start, trace_trace, b);
				if (0 == trace_trace.fraction)
					return ;
			}
		}
		
		/*
		* ================== CM_RecursiveHullCheck ==================
		*/
		public static void  CM_RecursiveHullCheck(int num, float p1f, float p2f, float[] p1, float[] p2)
		{
			cnode_t node;
			cplane_t plane;
			float t1, t2, offset;
			float frac, frac2;
			float idist;
			int i;
			int side;
			float midf;
			
			if (trace_trace.fraction <= p1f)
				return ; // already hit something nearer
			
			// if < 0, we are in a leaf node
			if (num < 0)
			{
				CM_TraceToLeaf(- 1 - num);
				return ;
			}
			
			//
			// find the point distances to the seperating plane
			// and the offset for the size of the box
			//
			node = map_nodes[num];
			plane = node.plane;
			
			if (plane.type < 3)
			{
				t1 = p1[plane.type] - plane.dist;
				t2 = p2[plane.type] - plane.dist;
				offset = trace_extents[plane.type];
			}
			else
			{
				t1 = Math3D.DotProduct(plane.normal, p1) - plane.dist;
				t2 = Math3D.DotProduct(plane.normal, p2) - plane.dist;
				if (trace_ispoint)
					offset = 0;
				else
					offset = System.Math.Abs(trace_extents[0] * plane.normal[0]) + System.Math.Abs(trace_extents[1] * plane.normal[1]) + System.Math.Abs(trace_extents[2] * plane.normal[2]);
			}
			
			// see which sides we need to consider
			if (t1 >= offset && t2 >= offset)
			{
				CM_RecursiveHullCheck(node.children[0], p1f, p2f, p1, p2);
				return ;
			}
			if (t1 < - offset && t2 < - offset)
			{
				CM_RecursiveHullCheck(node.children[1], p1f, p2f, p1, p2);
				return ;
			}
			
			// put the crosspoint DIST_EPSILON pixels on the near side
			if (t1 < t2)
			{
				idist = 1.0f / (t1 - t2);
				side = 1;
				frac2 = (t1 + offset + DIST_EPSILON) * idist;
				frac = (t1 - offset + DIST_EPSILON) * idist;
			}
			else if (t1 > t2)
			{
				idist = 1.0f / (t1 - t2);
				side = 0;
				frac2 = (t1 - offset - DIST_EPSILON) * idist;
				frac = (t1 + offset + DIST_EPSILON) * idist;
			}
			else
			{
				side = 0;
				frac = 1;
				frac2 = 0;
			}
			
			// move up to the node
			if (frac < 0)
				frac = 0;
			if (frac > 1)
				frac = 1;
			
			midf = p1f + (p2f - p1f) * frac;
			float[] mid = Vec3Cache.get_Renamed();
			
			for (i = 0; i < 3; i++)
				mid[i] = p1[i] + frac * (p2[i] - p1[i]);
			
			CM_RecursiveHullCheck(node.children[side], p1f, midf, p1, mid);
			
			// go past the node
			if (frac2 < 0)
				frac2 = 0;
			if (frac2 > 1)
				frac2 = 1;
			
			midf = p1f + (p2f - p1f) * frac2;
			for (i = 0; i < 3; i++)
				mid[i] = p1[i] + frac2 * (p2[i] - p1[i]);
			
			CM_RecursiveHullCheck(node.children[side ^ 1], midf, p2f, mid, p2);
			Vec3Cache.release();
		}
		
		//======================================================================
		
		/*
		* ================== CM_BoxTrace ==================
		*/
		public static trace_t BoxTrace(float[] start, float[] end, float[] mins, float[] maxs, int headnode, int brushmask)
		{
			
			// for multi-check avoidance
			checkcount++;
			
			// for statistics, may be zeroed
			Globals.c_traces++;
			
			// fill in a default trace
			//was: memset(& trace_trace, 0, sizeof(trace_trace));
			trace_trace = new trace_t();
			
			trace_trace.fraction = 1;
			trace_trace.surface = nullsurface.c;
			
			if (numnodes == 0)
			{
				// map not loaded
				return trace_trace;
			}
			
			trace_contents = brushmask;
			Math3D.VectorCopy(start, trace_start);
			Math3D.VectorCopy(end, trace_end);
			Math3D.VectorCopy(mins, trace_mins);
			Math3D.VectorCopy(maxs, trace_maxs);
			
			//
			// check for position test special case
			//
			if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2])
			{
				
				int[] leafs = new int[1024];
				int i, numleafs;
				float[] c1 = new float[]{0, 0, 0}, c2 = new float[]{0, 0, 0};
				int topnode = 0;
				
				Math3D.VectorAdd(start, mins, c1);
				Math3D.VectorAdd(start, maxs, c2);
				
				for (i = 0; i < 3; i++)
				{
					c1[i] -= 1;
					c2[i] += 1;
				}
				
				int[] tn = new int[]{topnode};
				
				numleafs = CM_BoxLeafnums_headnode(c1, c2, leafs, 1024, headnode, tn);
				topnode = tn[0];
				for (i = 0; i < numleafs; i++)
				{
					CM_TestInLeaf(leafs[i]);
					if (trace_trace.allsolid)
						break;
				}
				Math3D.VectorCopy(start, trace_trace.endpos);
				return trace_trace;
			}
			
			//
			// check for point special case
			//
			if (mins[0] == 0 && mins[1] == 0 && mins[2] == 0 && maxs[0] == 0 && maxs[1] == 0 && maxs[2] == 0)
			{
				trace_ispoint = true;
				Math3D.VectorClear(trace_extents);
			}
			else
			{
				trace_ispoint = false;
				trace_extents[0] = - mins[0] > maxs[0]?- mins[0]:maxs[0];
				trace_extents[1] = - mins[1] > maxs[1]?- mins[1]:maxs[1];
				trace_extents[2] = - mins[2] > maxs[2]?- mins[2]:maxs[2];
			}
			
			//
			// general sweeping through world
			//
			CM_RecursiveHullCheck(headnode, 0, 1, start, end);
			
			if (trace_trace.fraction == 1)
			{
				Math3D.VectorCopy(end, trace_trace.endpos);
			}
			else
			{
				for (int i = 0; i < 3; i++)
					trace_trace.endpos[i] = start[i] + trace_trace.fraction * (end[i] - start[i]);
			}
			return trace_trace;
		}
		
		/*
		* ================== CM_TransformedBoxTrace
		* 
		* Handles offseting and rotation of the end points for moving and rotating
		* entities ==================
		*/
		public static trace_t TransformedBoxTrace(float[] start, float[] end, float[] mins, float[] maxs, int headnode, int brushmask, float[] origin, float[] angles)
		{
			trace_t trace;
			float[] start_l = new float[]{0, 0, 0}, end_l = new float[]{0, 0, 0};
			float[] a = new float[]{0, 0, 0};
			float[] forward = new float[]{0, 0, 0}, right = new float[]{0, 0, 0}, up = new float[]{0, 0, 0};
			float[] temp = new float[]{0, 0, 0};
			bool rotated;
			
			// subtract origin offset
			Math3D.VectorSubtract(start, origin, start_l);
			Math3D.VectorSubtract(end, origin, end_l);
			
			// rotate start and end into the models frame of reference
			if (headnode != box_headnode && (angles[0] != 0 || angles[1] != 0 || angles[2] != 0))
				rotated = true;
			else
				rotated = false;
			
			if (rotated)
			{
				Math3D.AngleVectors(angles, forward, right, up);
				
				Math3D.VectorCopy(start_l, temp);
				start_l[0] = Math3D.DotProduct(temp, forward);
				start_l[1] = - Math3D.DotProduct(temp, right);
				start_l[2] = Math3D.DotProduct(temp, up);
				
				Math3D.VectorCopy(end_l, temp);
				end_l[0] = Math3D.DotProduct(temp, forward);
				end_l[1] = - Math3D.DotProduct(temp, right);
				end_l[2] = Math3D.DotProduct(temp, up);
			}
			
			// sweep the box through the model
			trace = BoxTrace(start_l, end_l, mins, maxs, headnode, brushmask);
			
			if (rotated && trace.fraction != 1.0)
			{
				// FIXME: figure out how to do this with existing angles
				Math3D.VectorNegate(angles, a);
				Math3D.AngleVectors(a, forward, right, up);
				
				Math3D.VectorCopy(trace.plane.normal, temp);
				trace.plane.normal[0] = Math3D.DotProduct(temp, forward);
				trace.plane.normal[1] = - Math3D.DotProduct(temp, right);
				trace.plane.normal[2] = Math3D.DotProduct(temp, up);
			}
			
			trace.endpos[0] = start[0] + trace.fraction * (end[0] - start[0]);
			trace.endpos[1] = start[1] + trace.fraction * (end[1] - start[1]);
			trace.endpos[2] = start[2] + trace.fraction * (end[2] - start[2]);
			
			return trace;
		}
		
		/*
		* ===============================================================================
		* PVS / PHS
		* ===============================================================================
		*/
		
		/*
		* =================== CM_DecompressVis ===================
		*/
		public static void  CM_DecompressVis(sbyte[] in_Renamed, int offset, sbyte[] out_Renamed)
		{
			int c;
			
			int row;
			
			row = (numclusters + 7) >> 3;
			int outp = 0;
			int inp = offset;
			
			if (in_Renamed == null || numvisibility == 0)
			{
				// no vis info, so make all
				// visible
				while (row != 0)
				{
					out_Renamed[outp++] = (sbyte) SupportClass.Identity(0xFF);
					row--;
				}
				return ;
			}
			
			do 
			{
				if (in_Renamed[inp] != 0)
				{
					out_Renamed[outp++] = in_Renamed[inp++];
					continue;
				}
				
				c = in_Renamed[inp + 1] & 0xFF;
				inp += 2;
				if (outp + c > row)
				{
					c = row - (outp);
					Com.DPrintf("warning: Vis decompression overrun\n");
				}
				while (c != 0)
				{
					out_Renamed[outp++] = 0;
					c--;
				}
			}
			while (outp < row);
		}
		
		//UPGRADE_NOTE: The initialization of  'pvsrow' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static sbyte[] pvsrow;
		
		//UPGRADE_NOTE: The initialization of  'phsrow' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		public static sbyte[] phsrow;
		
		public static sbyte[] CM_ClusterPVS(int cluster)
		{
			if (cluster == - 1)
				SupportClass.ArraySupport.Fill(SupportClass.ToByteArray(pvsrow), 0, (numclusters + 7) >> 3, (byte) 0);
			else
				CM_DecompressVis(map_visibility, map_vis.bitofs[cluster][Defines.DVIS_PVS], pvsrow);
			return pvsrow;
		}
		
		public static sbyte[] CM_ClusterPHS(int cluster)
		{
			if (cluster == - 1)
				SupportClass.ArraySupport.Fill(SupportClass.ToByteArray(phsrow), 0, (numclusters + 7) >> 3, (byte) 0);
			else
				CM_DecompressVis(map_visibility, map_vis.bitofs[cluster][Defines.DVIS_PHS], phsrow);
			return phsrow;
		}
		
		/*
		* ===============================================================================
		* AREAPORTALS
		* ===============================================================================
		*/
		
		public static void  FloodArea_r(carea_t area, int floodnum)
		{
			//Com.Printf("FloodArea_r(" + floodnum + ")...\n");
			int i;
			qfiles.dareaportal_t p;
			
			if (area.floodvalid == floodvalid)
			{
				if (area.floodnum == floodnum)
					return ;
				Com.Error(Defines.ERR_DROP, "FloodArea_r: reflooded");
			}
			
			area.floodnum = floodnum;
			area.floodvalid = floodvalid;
			
			for (i = 0; i < area.numareaportals; i++)
			{
				p = map_areaportals[area.firstareaportal + i];
				if (portalopen[p.portalnum])
					FloodArea_r(map_areas[p.otherarea], floodnum);
			}
		}
		
		/*
		* ==================== FloodAreaConnections ====================
		*/
		public static void  FloodAreaConnections()
		{
			Com.DPrintf("FloodAreaConnections...\n");
			
			int i;
			carea_t area;
			int floodnum;
			
			// all current floods are now invalid
			floodvalid++;
			floodnum = 0;
			
			// area 0 is not used
			for (i = 1; i < numareas; i++)
			{
				
				area = map_areas[i];
				
				if (area.floodvalid == floodvalid)
					continue; // already flooded into
				floodnum++;
				FloodArea_r(area, floodnum);
			}
		}
		
		/*
		* ================= CM_SetAreaPortalState =================
		*/
		public static void  CM_SetAreaPortalState(int portalnum, bool open)
		{
			if (portalnum > numareaportals)
				Com.Error(Defines.ERR_DROP, "areaportal > numareaportals");
			
			portalopen[portalnum] = open;
			FloodAreaConnections();
		}
		
		/*
		* ================= CM_AreasConnected =================
		*/
		
		public static bool CM_AreasConnected(int area1, int area2)
		{
			if (map_noareas.value_Renamed != 0)
				return true;
			
			if (area1 > numareas || area2 > numareas)
				Com.Error(Defines.ERR_DROP, "area > numareas");
			
			if (map_areas[area1].floodnum == map_areas[area2].floodnum)
				return true;
			
			return false;
		}
		
		/*
		* ================= CM_WriteAreaBits
		* 
		* Writes a length byte followed by a bit vector of all the areas that area
		* in the same flood as the area parameter
		* 
		* This is used by the client refreshes to cull visibility =================
		*/
		public static int CM_WriteAreaBits(sbyte[] buffer, int area)
		{
			int i;
			int floodnum;
			int bytes;
			
			bytes = (numareas + 7) >> 3;
			
			if (map_noareas.value_Renamed != 0)
			{
				// for debugging, send everything
				SupportClass.ArraySupport.Fill(SupportClass.ToByteArray(buffer), 0, bytes, (byte) SupportClass.Identity(255));
			}
			else
			{
				SupportClass.ArraySupport.Fill(SupportClass.ToByteArray(buffer), 0, bytes, (byte) 0);
				floodnum = map_areas[area].floodnum;
				for (i = 0; i < numareas; i++)
				{
					if (map_areas[i].floodnum == floodnum || area == 0)
						buffer[i >> 3] |= (sbyte) (1 << (i & 7));
				}
			}
			
			return bytes;
		}
		
		/*
		* =================== CM_WritePortalState
		* 
		* Writes the portal state to a savegame file ===================
		*/
		
		//UPGRADE_TODO: Class 'java.io.RandomAccessFile' was converted to 'System.IO.FileStream' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioRandomAccessFile'"
		public static void  CM_WritePortalState(System.IO.FileStream os)
		{
			
			//was: fwrite(portalopen, sizeof(portalopen), 1, f);
			try
			{
				
				for (int n = 0; n < portalopen.Length; n++)
					if (portalopen[n])
					{
						System.IO.BinaryWriter temp_BinaryWriter;
						temp_BinaryWriter = new System.IO.BinaryWriter(os);
						temp_BinaryWriter.Write((System.Int32) 1);
					}
					else
					{
						System.IO.BinaryWriter temp_BinaryWriter2;
						temp_BinaryWriter2 = new System.IO.BinaryWriter(os);
						temp_BinaryWriter2.Write((System.Int32) 0);
					}
			}
			catch (System.Exception e)
			{
				//UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.toString' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"
				Com.Printf("ERROR:" + e);
				SupportClass.WriteStackTrace(e, Console.Error);
			}
		}
		
		/*
		* =================== CM_ReadPortalState
		* 
		* Reads the portal state from a savegame file and recalculates the area
		* connections ===================
		*/
		//UPGRADE_TODO: Class 'java.io.RandomAccessFile' was converted to 'System.IO.FileStream' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javaioRandomAccessFile'"
		public static void  CM_ReadPortalState(System.IO.FileStream f)
		{
			
			//was: FS_Read(portalopen, sizeof(portalopen), f);
			int len = portalopen.Length * 4;
			
			sbyte[] buf = new sbyte[len];
			
			FS.Read(buf, len, f);
			
			ByteBuffer bb = ByteBuffer.wrap(buf);
			IntBuffer ib = bb.asIntBuffer();
			
			for (int n = 0; n < portalopen.Length; n++)
				portalopen[n] = ib.get_Renamed() != 0;
			
			FloodAreaConnections();
		}
		
		/*
		* ============= CM_HeadnodeVisible
		* 
		* Returns true if any leaf under headnode has a cluster that is potentially
		* visible =============
		*/
		public static bool CM_HeadnodeVisible(int nodenum, sbyte[] visbits)
		{
			int leafnum;
			int cluster;
			cnode_t node;
			
			if (nodenum < 0)
			{
				leafnum = - 1 - nodenum;
				cluster = map_leafs[leafnum].cluster;
				if (cluster == - 1)
					return false;
				if (0 != (visbits[SupportClass.URShift(cluster, 3)] & (1 << (cluster & 7))))
					return true;
				return false;
			}
			
			node = map_nodes[nodenum];
			if (CM_HeadnodeVisible(node.children[0], visbits))
				return true;
			return CM_HeadnodeVisible(node.children[1], visbits);
		}
		static CM()
		{
			map_brushsides = new cbrushside_t[Defines.MAX_MAP_BRUSHSIDES];
			{
				for (int n = 0; n < Defines.MAX_MAP_BRUSHSIDES; n++)
					map_brushsides[n] = new cbrushside_t();
			}
			map_surfaces = new mapsurface_t[Defines.MAX_MAP_TEXINFO];
			{
				for (int n = 0; n < Defines.MAX_MAP_TEXINFO; n++)
					map_surfaces[n] = new mapsurface_t();
			}
			map_planes = new cplane_t[Defines.MAX_MAP_PLANES + 6];
			{
				for (int n = 0; n < Defines.MAX_MAP_PLANES + 6; n++)
					map_planes[n] = new cplane_t();
			}
			map_nodes = new cnode_t[Defines.MAX_MAP_NODES + 6];
			{
				for (int n = 0; n < Defines.MAX_MAP_NODES + 6; n++)
					map_nodes[n] = new cnode_t();
			}
			map_leafs = new cleaf_t[Defines.MAX_MAP_LEAFS];
			{
				for (int n = 0; n < Defines.MAX_MAP_LEAFS; n++)
					map_leafs[n] = new cleaf_t();
			}
			map_leafbrushes = new int[Defines.MAX_MAP_LEAFBRUSHES];
			map_cmodels = new cmodel_t[Defines.MAX_MAP_MODELS];
			{
				for (int n = 0; n < Defines.MAX_MAP_MODELS; n++)
					map_cmodels[n] = new cmodel_t();
			}
			map_brushes = new cbrush_t[Defines.MAX_MAP_BRUSHES];
			{
				for (int n = 0; n < Defines.MAX_MAP_BRUSHES; n++)
					map_brushes[n] = new cbrush_t();
			}
			map_visibility = new sbyte[Defines.MAX_MAP_VISIBILITY];
			map_areas = new carea_t[Defines.MAX_MAP_AREAS];
			{
				for (int n = 0; n < Defines.MAX_MAP_AREAS; n++)
					map_areas[n] = new carea_t();
			}
			map_areaportals = new qfiles.dareaportal_t[Defines.MAX_MAP_AREAPORTALS];
			{
				for (int n = 0; n < Defines.MAX_MAP_AREAPORTALS; n++)
					map_areaportals[n] = new qfiles.dareaportal_t();
			}
			portalopen = new bool[Defines.MAX_MAP_AREAPORTALS];
			pvsrow = new sbyte[Defines.MAX_MAP_LEAFS / 8];
			phsrow = new sbyte[Defines.MAX_MAP_LEAFS / 8];
		}
	}
}